import struct
import time
from socket import socket
from typing import Tuple, List, Iterable

import eventlet
from django.conf import settings


def zfill(s: str or bytes, width: int, left: bool = False, fill=b'\x00') -> bytes:
    if not width:
        return s
    left_length = width - len(s)
    if left_length < 0:
        return s
    fill_str = fill * left_length
    return fill_str + s if left else s + fill_str


def itob(i: int, byteorder='big') -> bytes:
    '''
    int 转 bytes
    :param i:
    :return:
    '''
    if byteorder == 'little':
        res = struct.pack('<i', i).strip(b'\x00')
    else:
        res = struct.pack('>i', i).strip(b'\x00')
    return res


def btoi(b: bytes, bytesorder='big', signed=True) -> int:
    '''
    bytes转int
    :param b:
    :return:
    '''
    return int.from_bytes(b, signed=signed, byteorder=bytesorder)


def split_body(data: bytes, place: Iterable, decode=True) -> List[str] or List[bytes]:
    '''
    分割bytes
    :param data:  需要分割的data
    :param place: 字段长度  例如(1, 2, 4) 会返回  [bytes<length=1>,bytes<length=2>,bytes<length=4>]
    :param decode: 是否将分割的bytes转换成为str
    :return: List[str] or List[bytes]
    '''
    start = 0
    res = []
    for i in place:
        split_data = data[start: start + i]
        res.append(str(split_data, encoding='utf-8') if decode else split_data)
        start = start + i
    return res


def unpack(data_str: str or bytes) -> Tuple[str or bytes, str or bytes]:
    # 取出一个字节长度的msgtype
    msg_tpe = data_str[:1]
    # 取出data
    data = data_str[1:]
    return msg_tpe, data


def which_company(data: str or bytes) -> str:
    '''
    判断是哪一家公司的数据
    zhiling  自家公司
    ...
    ...
    ...

    :param data:
    :return:
    '''
    return 'zhiling'


def fill_data(s: str or int or bytes, width: int, left=False) -> bytes:
    '''
    将数据补位成宽度大小的bytes  int像左边补0  str  bytes向右边补0
    :param s:
    :param width:
    :return:
    '''
    if isinstance(s, bytes):
        return zfill(s, width, left=left)
    if isinstance(s, int):
        s = itob(s, byteorder='little' if left else 'big')
        return zfill(bytes(s), width, left=not left)
    return zfill(bytes(s, encoding='utf-8'), width, left=left)


def pack_all(s: bytes) -> bytes:
    '''
    为数据外层打包  逻辑为      s = (payload_type,payload)   return start + length + s + sign + end
    :param s:
    :return:
    '''
    # 将长度转换成16进制数据补位



def read_no_wait(conn: socket):
    head = conn.recv(2)
    if head == b'':
        conn.close()
        raise OSError('connect is close')
    payload_length = conn.recv(2)
    payload_length = btoi(payload_length)
    payload = conn.recv(payload_length)
    (
        msgid,
        total_package,
        current_package,
        msg_type,
        data
    ) = (
        payload[:4],
        payload[4:6],
        payload[6:8],
        payload[8:9],
        payload[9:]
    )
    sign = conn.recv(1)
    end = conn.recv(2)
    assert len(payload) == payload_length, OSError('read faid')
    return msg_type, data, msgid


def read(conn: socket, time_out=settings.TASK_TIME_OUT):
    start = time.time()
    while True:
        try:
            head = conn.recv(2)
        except Exception as e:
            if time.time() - start > time_out and time_out != 0:
                raise BlockingIOError("no content")
            eventlet.sleep(0.2)
            continue
        if head != b'\x55\xaa':
            continue
        if head == b'':
            conn.close()
        payload_length = conn.recv(2)
        # print(len(payload_length),payload_length.hex())
        payload_length = btoi(payload_length)
        payload = conn.recv(payload_length)
        # print(payload_length)
        (
            msgid,
            total_package,
            current_package,
            msg_type,
            data
        ) = (
            payload[:4],
            payload[4:6],
            payload[6:8],
            payload[8:9],
            payload[9:]
        )
        sign = conn.recv(1)
        end = conn.recv(2)
        # print('=======================')
        # print(head.hex(),'head hex')
        # print(payload_length,'payload_length')
        # print(payload.hex(),'payload hex')
        # print(sign.hex(),'sign hex')
        # print(end,end.hex())
        # print('=======================')
        assert len(payload) == payload_length, OSError('read faid')
        assert end == b'\xaa\x55', Exception('read faid')
        return msg_type, data, msgid,total_package,current_package
